package cn.mw.cmdb.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.mw.cmdb.entity.*;
import cn.mw.cmdb.mongoMapper.AssetsScanResultMapper;
import cn.mw.cmdb.mongoMapper.AssetsScanTotalResultMapper;
import cn.mw.cmdb.param.*;
import cn.mw.cmdb.processor.PropertyValueManage;
import cn.mw.cmdb.util.AlphanumericComparator;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.components.mongodb.config.MongoConstant;
import cn.mw.components.mongodb.entity.CriteriaOpsType;
import cn.mw.components.mongodb.entity.SearchParam;
import cn.mw.components.mongodb.entity.SubCondition;
import cn.mw.components.mongodb.tool.CriteriaWrapper;
import cn.mw.microMonitorCommon.api.scan.AssetScanService;
import cn.mw.microMonitorCommon.api.user.MwUserApi;
import cn.mw.microMonitorCommon.enums.RuleType;
import cn.mw.microMonitorCommon.enums.SecurityLevel;
import cn.mw.microMonitorCommon.security.dto.MwUser;
import cn.mw.microMonitorCommon.security.utils.SecurityUtils;
import cn.mw.microMonitorCommon.util.IpUtil;
import cn.mw.microMonitorCommon.util.ModuleIDManager;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static cn.mw.cmdb.entity.ScanResultSuccessInfo.insertTypeAdd;
import static cn.mw.cmdb.entity.ScanResultSuccessInfo.insertTypeManage;
import static cn.mw.cmdb.processor.MonitorValue.*;
import static cn.mw.cmdb.util.CMDBCommonUtil.filterListByMap;
import static cn.mw.cmdb.util.CMDBCommonUtil.sortListByField;
import static cn.mw.cmdb.util.ValConvertUtil.booleanValueConvert;
import static cn.mw.cmdb.util.ValConvertUtil.intValueConvert;
import static cn.mw.cmdb.view.ViewConvert.ascSortModel;

@Slf4j
public class AssetsScanResultServiceImpl extends ModelBaseServiceImpl<AssetsScanResultInfo, Object> {

    public static final String ID = "assetsScanResult";

    public static final String SUCCESS_PREFIX = "successList";
    public static final String FAIL_PREFIX = "failList";

    public static final String SUCCESS = "success";

    private CmdbServiceManage cmdbServiceManage;
    private AssetsScanResultMapper assetsScanResultMapper;
    private AssetsScanTotalResultMapper totalResultMapper;
    private ModuleIDManager moduleIDManager;

    private final AssetScanService assetScanService;

    private final MwUserApi mwUserApi;


    public AssetsScanResultServiceImpl(AssetsScanResultMapper assetsScanResultMapper, CmdbServiceManage cmdbServiceManage,
                                       ModuleIDManager moduleIDManager, AssetScanService assetScanService, MwUserApi mwUserApi,
                                       AssetsScanTotalResultMapper totalResultMapper) {
        this.assetsScanResultMapper = assetsScanResultMapper;
        this.cmdbServiceManage = cmdbServiceManage;
        this.moduleIDManager = moduleIDManager;
        this.assetScanService = assetScanService;
        this.mwUserApi = mwUserApi;
        this.totalResultMapper = totalResultMapper;
    }

    @Override
    public AssetsScanResultInfo genObject() {
        return new AssetsScanResultInfo();
    }

    @Override
    Object doAdd(AssetsScanResultInfo param) throws Exception {
        addOrUpdate(param);
        return true;
    }

    @Override
    boolean doUpdate(AssetsScanResultInfo param) throws Exception {
        addOrUpdate(param);
        return true;
    }

    @Override
    boolean doRemove(AssetsScanResultInfo param) throws Exception {
        //删除资产监控
        CMDBTool.remove(assetsScanResultMapper, param.getId(), AssetsScanResultInfo.class, true);
        return true;
    }

    @Override
    Object doBatchAdd(Object params) throws Exception {
        if (params instanceof List) {
            List<AssetsScanResultInfo> params1 = (List<AssetsScanResultInfo>) params;
            this.assetsScanResultMapper.insertAll(params1);
        }
        return true;
    }

    @Override
    boolean doBatchUpdate(Object params) {
        return false;
    }

    @Override
    AssetsScanResultInfo doSelectById(Object id) throws Exception {
        AssetsInstanceScanSearchParam param = (AssetsInstanceScanSearchParam) id;
        Criteria normalCriteria = new Criteria(CMDBTool.getFieldName(AssetsScanResultInfo::getScanRuleId))
                .is(param.getId()).and(CMDBTool.getFieldName(AssetsScanResultInfo::getBatchNo)).is(param.getBatchNo());
        Query query = new Query();
        query.addCriteria(normalCriteria);
        AssetsScanResultInfo one = this.assetsScanResultMapper.findOne(query);
        return one;
    }

    @Override
    public List<AssetsScanResultInfo> doSelectList(Object param) throws Exception {
        AssetsInstanceScanSearchParam searchParam = (AssetsInstanceScanSearchParam) param;
        List<Criteria> criteriaList = new ArrayList<>();
        Criteria normalCriteria = new Criteria(CMDBTool.getFieldName(AssetsScanResultInfo::getScanRuleId)).is(searchParam.getId());
        criteriaList.add(normalCriteria);
        if (null != searchParam.getBatchNo()) {
            Criteria criteria = new Criteria(CMDBTool.getFieldName(AssetsScanResultInfo::getBatchNo)).is(searchParam.getBatchNo());
            criteriaList.add(criteria);
        }
        String queryIdent = searchParam.getQueryIdent();
        if (StrUtil.isNotBlank(queryIdent)) {
            List<Criteria> subList = SUCCESS.equals(queryIdent) ? generatorSuccessCondition(searchParam) : generatorFailedCondition(searchParam);
            if (CollUtil.isNotEmpty(subList)) {
                criteriaList.addAll(subList);
            }
        }

        Criteria searchCriteria = new Criteria().andOperator(criteriaList);
        List<AssetsScanResultInfo> ret = CMDBTool.selectAuthorizedList(AssetsScanResultInfo.class, assetsScanResultMapper, searchCriteria, searchParam);
        scanResultListSort(ret, searchParam);

        return ret;
    }

    private List<Criteria> generatorFailedCondition(AssetsInstanceScanSearchParam searchParam) {
        return searchParam.generatorCriteria(FAIL_PREFIX);
    }

    private List<Criteria> generatorSuccessCondition(AssetsInstanceScanSearchParam searchParam) {
        return searchParam.generatorCriteria(SUCCESS_PREFIX);
    }

    private void scanResultListSort(List<AssetsScanResultInfo> ret, AssetsInstanceScanSearchParam searchParam) {
        if (CollectionUtils.isNotEmpty(ret)) {
            for (AssetsScanResultInfo resultInfo : ret) {
                List<ScanResultSuccessInfo> successList = resultInfo.getSuccessList();
                setIpLongIfNull(successList, ScanResultSuccessInfo::getIpLong, ScanResultSuccessInfo::setIpLong, ScanResultSuccessInfo::getIpAddress);

                List<ScanResultFailInfo> failList = resultInfo.getFailList();
                setIpLongIfNull(failList, ScanResultFailInfo::getIpLong, ScanResultFailInfo::setIpLong, ScanResultFailInfo::getIpAddress);
                if (com.alibaba.nacos.common.utils.CollectionUtils.isNotEmpty(successList) && (!Strings.isNullOrEmpty(searchParam.getSortName()) && !Strings.isNullOrEmpty(searchParam.getSortOrder()))) {
                    boolean isAsc = ascSortModel.equals(searchParam.getSortOrder()) ? true : false;
                    //列表字段排序
                    sortListByField(successList, searchParam.getSortName(), isAsc);
                    //列表字段排序
                    sortListByField(failList, searchParam.getSortName(), isAsc);
                }
            }
        }
    }


    @Override
    boolean doBatchRemove(Object params) throws Exception {
        AssetsDiscoverScanResultIdsParam param = (AssetsDiscoverScanResultIdsParam) params;
        Query query = new Query();
        if (CollectionUtil.isNotEmpty(param.getIds())) {
            query = new Query(CriteriaWrapper.where(AssetsScanResultInfo::getId).in(param.getIds()));
        }
        if (CollectionUtil.isNotEmpty(param.getScanRuleIds())) {
            query = new Query(CriteriaWrapper.where(AssetsScanResultInfo::getScanRuleId).in(param.getScanRuleIds()));
        }
        assetsScanResultMapper.remove(query);
        return true;
    }

    public void addOrUpdate(AssetsScanResultInfo param) throws Exception {
        Update update = new Update();
        update.set(CMDBTool.getFieldName(AssetsScanResultInfo::getSuccessList), param.getSuccessList());
        update.set(CMDBTool.getFieldName(AssetsScanResultInfo::getFailList), param.getFailList());
        Query query = new Query(CriteriaWrapper.where(AssetsScanResultInfo::getScanRuleId).is(param.getScanRuleId()));
        //id存在就更新，不存在则新增
        this.assetsScanResultMapper.upsert(query, update);
    }

    private Criteria doCriteria(AssetsInstanceScanSearchParam param) {
        String instanceId = param.getId();
        String ipAddress = param.getIpAddress();
        String brandId = param.getBrandId();

        SearchParam newSearchParam = new AssetsInstanceScanSearchParam();
        List<SearchParam> searchParamList = Lists.newArrayList();

        if (StrUtil.isNotBlank(instanceId)) {
            AssetsInstanceScanSearchParam instanceIdSearch = new AssetsInstanceScanSearchParam();
            instanceIdSearch.setOperation(CriteriaOpsType.and.name());
            instanceIdSearch.setId(instanceId);
            searchParamList.add(instanceIdSearch);
        }

        Map<String, SubCondition> condition = new HashMap<>();
        SubCondition subCondition = new SubCondition();
        AssetsInstanceScanSearchParam sub = new AssetsInstanceScanSearchParam();

        if (StrUtil.isNotBlank(ipAddress)) {
            sub.setOperation(CriteriaOpsType.and.name());
            new SubCondition(CriteriaOpsType.in.name(), param.getIpAddress());
        }
        if (SUCCESS_PREFIX.equals(param.getQueryIdent())) {
            condition.put(CMDBTool.getFieldName(AssetsScanResultInfo::getSuccessList), subCondition);
            sub.setCondition(condition);
            searchParamList.add(sub);

            if (StrUtil.isNotBlank(brandId)) {
                AssetsInstanceScanSearchParam brandIdSearch = new AssetsInstanceScanSearchParam();
                brandIdSearch.setOperation(CriteriaOpsType.and.name());
                brandIdSearch.setBrandId(brandId);
                searchParamList.add(brandIdSearch);
            }
            newSearchParam.setSearchParamList(searchParamList);
            newSearchParam.setPage(param.getPage());
            newSearchParam.setOperation(CriteriaOpsType.and.name());

            // 获取参数值对对应属性类型,并生成查询条件
            Criteria criteria = genCriteria(newSearchParam);
            return criteria;
        }

        condition.put(CMDBTool.getFieldName(AssetsScanResultInfo::getFailList), subCondition);
        sub.setCondition(condition);
        searchParamList.add(sub);
        if (StrUtil.isNotBlank(param.getFailReason())) {
            sub = new AssetsInstanceScanSearchParam();
            sub.setOperation(CriteriaOpsType.and.name());
            subCondition = new SubCondition(CriteriaOpsType.in.name(), param.getFailReason());
            condition = new HashMap<>();
            condition.put(CMDBTool.getFieldName(AssetsScanResultInfo::getFailList), subCondition);
            sub.setCondition(condition);
            searchParamList.add(sub);
        }
        Criteria criteria = genCriteria(newSearchParam);
        return criteria;
    }


    public List<InstanceInfo> instanceConvert(List<ScanResultSuccessInfo> scanResultSuccessList, ModelAddTangAssetsParam param) throws Exception {
        List<InstanceInfo> infoList = Lists.newArrayList();
        MwUser user = SecurityUtils.getUser();
        for (ScanResultSuccessInfo scanResultSuccess : scanResultSuccessList) {
            InstanceInfo assetsInstanceInfo = instanceConvert(scanResultSuccess, param, user);
            infoList.add(assetsInstanceInfo);
        }
        return infoList;
    }

    public InstanceInfo instanceConvert(ScanResultSuccessInfo scanResultSuccess, ModelAddTangAssetsParam param, MwUser user) {
        InstanceInfo instanceInfo = new InstanceInfo();
        instanceInfo.setInstanceName(scanResultSuccess.getInstanceName());
        instanceInfo.setModelId(scanResultSuccess.getModelId());
        instanceInfo.setInBandIp(scanResultSuccess.getIpAddress());
        instanceInfo.setTaskType("batch");
        instanceInfo.setGroupId(scanResultSuccess.getGroupId());
        instanceInfo.setBrandId(scanResultSuccess.getBrandId());
        instanceInfo.setUsers(param.getUsers());
        instanceInfo.setOrgs(param.getOrgs());
        instanceInfo.setDeviceCode(scanResultSuccess.getDeviceCode());
        if (CollectionUtil.isNotEmpty(param.getGroups())) {
            instanceInfo.setGroups(param.getGroups());
        }

        if (ObjUtil.isNotEmpty(user) && user.getId() != null) {
            Long id = user.getId();
            instanceInfo.setCreater(id);
            instanceInfo.setModifier(id);
        }
        instanceInfo.setCreateTime(scanResultSuccess.getCreateDate());
        instanceInfo.setModificationTime(scanResultSuccess.getModificationDate());

        Map<String, Object> data = new HashMap<>();
        data.put(InBandIpKey, instanceInfo.getInBandIp());
        data.put(InStanceNameKey, instanceInfo.getInstanceName());
        if (null != param.getProxyType()) {
            data.put(ProxyTypeKey, param.getProxyType());
        }

        if (CollectionUtil.isNotEmpty(param.getFunctionModuleList())) {
            data.put(CMDBTool.getFieldName(ModelInfo::getFunctionModule), param.getFunctionModuleList());
        }
        RuleType ruleType = scanResultSuccess.getRuleType();
        if (ruleType != null) {
            Map<String, Object> monitorProperty = getMonitorProperty(ruleType, scanResultSuccess, param.getCustomPortList());
            data.putAll(monitorProperty);
        }

        if (StrUtil.isNotBlank(param.getMonitorServerId())) {
            data.put(MonitorServerIdKey, Integer.parseInt(param.getMonitorServerId()));
        }
        if (StrUtil.isNotBlank(scanResultSuccess.getSpecification())) {
            data.put(SpecificationKey, scanResultSuccess.getSpecificationId());
        }
        if (null != param.getCheckNow()) {
            data.put(CheckNowFlagKey, param.getCheckNow());
        }
        if (null != param.getRandomNameFlag() && Boolean.TRUE.equals(param.getRandomNameFlag())) {
            instanceInfo.setRandomNameFlag(param.getRandomNameFlag());
        }
        if (null != param.getMw_trunkMonitorFlag()) {
            data.put(TrunkMonitorFlagKey, booleanValueConvert(param.getMw_trunkMonitorFlag()));
        }
        if (null != param.getOperationMonitor()) {
            data.put(ZabbixStatusKey, param.getOperationMonitor());
        }
        if (StrUtil.isNotBlank(param.getEngineId())) {
            data.put(PollingEngineKey, param.getEngineId());
        }


        instanceInfo.setData(data);
        return instanceInfo;
    }

    private Map<String, Object> getMonitorProperty(RuleType ruleType, ScanResultSuccessInfo scanResultSuccess,
                                                   List<ModelAddTangAssetsParam.CustomNanotubeType> customPortList) {
        Map<String, Object> dataMap = new HashMap<>();

        String ruleTypeName = ruleType.getName();
        dataMap.put(RuleTypeKey, ruleTypeName);
        dataMap.put(PortKey, Integer.parseInt(ruleType.getPort()));

        // 自定义端口如果不为空, 使用自定义纳管端口, 否则使用默认的
        if (CollUtil.isNotEmpty(customPortList)) {
            for (ModelAddTangAssetsParam.CustomNanotubeType customNanotubeType : customPortList) {
                // 如果匹配则使用前端传过来的type, 否则使用默认的
                if (ruleTypeName.contains(customNanotubeType.getType()) || ruleTypeName.equalsIgnoreCase(customNanotubeType.getType())) {
                    dataMap.put(RuleTypeKey, customNanotubeType.getType());
                    dataMap.put(PortKey, customNanotubeType.getPort());
                    break;
                }
            }
        }
        // TemplateIdKey
        if (StrUtil.isNotBlank(scanResultSuccess.getTemplateId())) {
            dataMap.put(TemplateIdKey, scanResultSuccess.getTemplateId());
        }
        if (StrUtil.isNotBlank(scanResultSuccess.getMw_templateName())) {
            dataMap.put(TemplateNameKey, scanResultSuccess.getMw_templateName());
        }

        switch (ruleType) {
            case Agent -> dataMap.put(MonitorModeKey, RuleType.Agent.getMonitorMode());
            // v1, v2, v3是相同的monitorMode
            case SNMPv1, SNMPv2 -> {
                dataMap.put(MonitorModeKey, RuleType.SNMPv2.getMonitorMode());
                if (StrUtil.isNotBlank(scanResultSuccess.getCommunity())) {
                    dataMap.put(CommunityKey, scanResultSuccess.getCommunity());
                }
            }
            case SNMPv3 -> {
                // SecurityNameKey,SecurityLevelKey,ContextNameKey,
                // AuthAlgNameKey,PrivAlgNameKey,AuthAlgNameKey
                if (StrUtil.isNotBlank(scanResultSuccess.getSecurityName())) {
                    dataMap.put(SecurityNameKey, scanResultSuccess.getSecurityName());
                }
                SecurityLevel securityLevel = scanResultSuccess.getSecurityLevel();
                if (securityLevel != null) {
                    dataMap.put(SecurityLevelKey, securityLevel.getName());
                }
                if (StrUtil.isNotBlank(scanResultSuccess.getContextName())) {
                    dataMap.put(ContextNameKey, scanResultSuccess.getContextName());
                }

                // SNMP V3
                if (securityLevel != null) {
                    switch (securityLevel) {
                        case authPriv -> {
                            if (ObjUtil.isNotNull(scanResultSuccess.getAuthProtocol()) && StrUtil.isNotBlank(scanResultSuccess.getAuthProtocol().getName())) {
                                dataMap.put(AuthAlgNameKey, scanResultSuccess.getAuthProtocol().getName());
                            }
                            if (StrUtil.isNotBlank(scanResultSuccess.getAuthToken())) {
                                dataMap.put(AuthValueKey, scanResultSuccess.getAuthToken());
                            }
                            if (ObjUtil.isNotNull(scanResultSuccess.getPrivProtocol()) && StrUtil.isNotBlank(scanResultSuccess.getPrivProtocol().getName())) {
                                dataMap.put(PrivAlgNameKey, scanResultSuccess.getPrivProtocol().getName());
                            }
                            if (StrUtil.isNotBlank(scanResultSuccess.getPrivToken())) {
                                dataMap.put(PriValueKey, scanResultSuccess.getPrivToken());
                            }
                        }
                        case authNoPriv -> {
                            if (ObjUtil.isNotNull(scanResultSuccess.getAuthProtocol()) && StrUtil.isNotBlank(scanResultSuccess.getAuthProtocol().getName())) {
                                dataMap.put(AuthAlgNameKey, scanResultSuccess.getAuthProtocol().getName());
                            }
                            if (StrUtil.isNotBlank(scanResultSuccess.getAuthToken())) {
                                dataMap.put(AuthValueKey, scanResultSuccess.getAuthToken());
                            }
                        }
                        default -> {
                            log.error("unknown security level");
                        }
                    }
                }


                if (ObjUtil.isNotNull(scanResultSuccess.getAuthProtocol()) && StrUtil.isNotBlank(scanResultSuccess.getAuthProtocol().getName())) {
                    dataMap.put(AuthAlgNameKey, scanResultSuccess.getAuthProtocol().getName());
                }

            }
            case ICMP -> dataMap.put(MonitorModeKey, RuleType.ICMP.getMonitorMode());
            default -> {
                // 避免脏数据产生
                return new HashMap<>();
            }
        }
        return dataMap;
    }

    public List<AssetsScanTotalResultInfo> getInsScanResultTest(AssetsInstanceScanSearchParam param) throws Exception {
        //获取扫描信息
//        List<AssetsScanResultInfo> assetsScanResultInfoList = doSelectList(param);
        List<AssetsScanTotalResultInfo> scanTotalResultInfos = getScanTotalResultInfos(param.getId());

        if (CollectionUtil.isEmpty(scanTotalResultInfos)) {
            return Collections.emptyList();
        }
        Set<String> modelIds = new HashSet<>();
        Set<String> groupIds = new HashSet<>();
        List<String> succesIps = new ArrayList<>();
        Set<String> ruleTypeSet = new HashSet<>();
        // 获取group id 集合
        for (AssetsScanTotalResultInfo resultInfo : scanTotalResultInfos) {
            List<ScanResultSuccessInfo> successList = resultInfo.getSuccessList();
            if (CollectionUtil.isEmpty(successList)) {
                continue;
            }

            for (ScanResultSuccessInfo successInfo : successList) {
                if (StringUtils.hasText(successInfo.getModelId())) {
                    modelIds.add(successInfo.getModelId());
                }
                if (StringUtils.hasText(successInfo.getGroupId())) {
                    groupIds.add(successInfo.getGroupId());
                }

                if (StringUtils.hasText(successInfo.getIpAddress())) {
                    succesIps.add(successInfo.getIpAddress());
                }

                if (null != successInfo.getRuleType()) {
                    ruleTypeSet.add(successInfo.getRuleType().name());
                }
            }
        }

        // 查询模型组信息，获取模型组名
        CustomModelServiceImpl customModelService = (CustomModelServiceImpl) cmdbServiceManage.getModelService(CustomModelServiceImpl.ID);
        List<ModelInfo> modelInfos = customModelService.findByIds(new ArrayList<>(modelIds));
        Map<String, String> modelNameByIdMap = modelInfos.stream().collect(Collectors.toMap(ModelInfo::getId, ModelInfo::getModelName, (o1, o2) -> o1));

        ModelGroupServiceImpl groupService = (ModelGroupServiceImpl) cmdbServiceManage.getModelService(ModelGroupServiceImpl.ID);
        List<ModelGroupInfo> groupInfos = groupService.doSelectByIds(new ArrayList<>(groupIds));
        Map<String, String> groupNameByIdMap = groupInfos.stream().collect(Collectors.toMap(ModelGroupInfo::getId, ModelGroupInfo::getName, (o1, o2) -> o1));

        for (AssetsScanTotalResultInfo resultInfo : scanTotalResultInfos) {
            List<ScanResultSuccessInfo> successList = resultInfo.getSuccessList();
            if (CollectionUtil.isEmpty(successList)) {
                continue;
            }
            successDataExistCheck(successList);
            for (ScanResultSuccessInfo successInfo : successList) {
                String assetTypeName = "";
                String assetSubTypeName = "";

                if (StrUtil.isNotBlank(successInfo.getModelId())) {
                    // 匹配模型名
                    assetSubTypeName = modelNameByIdMap.get(successInfo.getModelId());
                }
                if (StrUtil.isBlank(assetTypeName) && StrUtil.isNotBlank(successInfo.getGroupId())) {
                    // 匹配组名
                    assetTypeName = groupNameByIdMap.get(successInfo.getGroupId());
                }
                successInfo.setAssetsTypeName(assetTypeName);
                successInfo.setAssetsSubTypeName(assetSubTypeName);
            }
            if (CMDBTool.getFieldName(ScanResultSuccessInfo::getExist).equals(param.getSortName())) {
                boolean isAsc = ascSortModel.equals(param.getSortOrder());
                //列表字段排序
                sortListByField(successList, param.getSortName(), isAsc);
            }
        }

        return scanTotalResultInfos;
    }


    public ScanAssetsResultCountParam getAssetsCountNumByRuleId(String ruleId) throws Exception {
        List<AssetsScanTotalResultInfo> scanTotalResultInfos = getScanTotalResultInfos(ruleId);
        long successNum = 0;
        long failNum = 0;
        long insertNum = 0;
        long noInsertNum = 0;
        ScanAssetsResultCountParam countParam = new ScanAssetsResultCountParam();
        if (CollectionUtils.isNotEmpty(scanTotalResultInfos)) {
            for (AssetsScanTotalResultInfo totalResultInfo : scanTotalResultInfos) {
                List<ScanResultSuccessInfo> successList = totalResultInfo.getSuccessList();
                successDataExistCheck(successList);
                insertNum += successList.stream().filter(s -> s.getExist()).count();
                noInsertNum += successList.stream().filter(s -> !s.getExist()).count();
                successNum += totalResultInfo.getSuccessList() != null ? totalResultInfo.getSuccessList().size() : 0;
                failNum += totalResultInfo.getFailList() != null ? totalResultInfo.getFailList().size() : 0;
            }
            countParam.setScanRuleId(ruleId);
            countParam.setSuccessNum(successNum);
            countParam.setInsertNum(insertNum);
            countParam.setNoInsertNum(noInsertNum);
            countParam.setFailNum(failNum);
        }
        return countParam;
    }

    public List<ScanResultSuccessInfo> getAllNoManageAssetsInfos(AssetsScanNoManageSearchParam params) throws Exception {
        AssetsInstanceScanSearchParam param = new AssetsInstanceScanSearchParam();
        List<AssetsScanTotalResultInfo> scanTotalResultInfos = getInsScanResultTest(param);
        List<ScanResultSuccessInfo> noInsertList = new ArrayList<>();
        for (AssetsScanTotalResultInfo resultInfo : scanTotalResultInfos) {
            List<ScanResultSuccessInfo> successList = resultInfo.getSuccessList();
            List<ScanResultSuccessInfo> collect = successList.stream().filter(s -> !s.getExist()).collect(Collectors.toList());
            noInsertList.addAll(collect);
        }
        List<ScanResultSuccessInfo> checkList = noInsertList.stream().filter(distinctByKey(s -> s.getIpAddress() + "_" + s.getHostName())).collect(Collectors.toList());
        //排序
        Collections.sort(checkList, new AlphanumericComparator(CMDBTool.getFieldName(ScanResultSuccessInfo::getHostName), params.getSortName(), params.getSortOrder()));
        if (params.getQueryField() != null && params.getQueryValue() != null) {

            Map<String, Object> ms = new HashMap<>();
            ms.put(params.getQueryField(), params.getQueryValue());
            //数据过滤
            checkList = filterListByMap(checkList, ms);
        }

        return checkList;
    }


    public List<AssetsScanResultInfo> getInsScanResult(AssetsInstanceScanSearchParam param) throws Exception {
        //获取扫描信息
        AssetsDiscoverServiceImpl assetsDiscoverService = (AssetsDiscoverServiceImpl) cmdbServiceManage.getModelService(AssetsDiscoverServiceImpl.ID);
        AssetsDiscoverInfo assetsDiscoverInfo = assetsDiscoverService.selectById(param.getId());
        if (null != assetsDiscoverInfo.getMaxBatchNo()) {
            param.setBatchNo(assetsDiscoverInfo.getMaxBatchNo());
        }

        List<AssetsScanResultInfo> assetsScanResultInfoList = doSelectList(param);
        if (CollectionUtil.isEmpty(assetsScanResultInfoList)) {
            return Collections.emptyList();
        }
        Set<String> modelIds = new HashSet<>();
        Set<String> groupIds = new HashSet<>();
        List<String> succesIps = new ArrayList<>();
        Set<String> ruleTypeSet = new HashSet<>();
        // 获取group id 集合
        for (AssetsScanResultInfo resultInfo : assetsScanResultInfoList) {
            List<ScanResultSuccessInfo> successList = resultInfo.getSuccessList();
            if (CollectionUtil.isEmpty(successList)) {
                continue;
            }

            for (ScanResultSuccessInfo successInfo : successList) {
                if (StringUtils.hasText(successInfo.getModelId())) {
                    modelIds.add(successInfo.getModelId());
                } else if (StringUtils.hasText(successInfo.getGroupId())) {
                    groupIds.add(successInfo.getGroupId());
                }

                if (StringUtils.hasText(successInfo.getIpAddress())) {
                    succesIps.add(successInfo.getIpAddress());
                }

                if (null != successInfo.getRuleType()) {
                    ruleTypeSet.add(successInfo.getRuleType().name());
                }
            }
        }

        //查询实例是否存在
        Set<String> instanceInfoKeys = new HashSet<>();
        if (!succesIps.isEmpty() && !ruleTypeSet.isEmpty()) {
            InstanceServiceImpl instanceService = (InstanceServiceImpl) cmdbServiceManage.getModelService(InstanceServiceImpl.ID);
            List<Criteria> criteriaList = new ArrayList<>();
            Criteria ipCriteria = CriteriaWrapper.where(InstanceInfo::getInBandIp).in(succesIps);
            criteriaList.add(ipCriteria);

            String criteriaField = InstanceInfo.SearchPrefix + MongoConstant.FieldSep +
                    PropertyValueManage.Prefix + CriteriaWrapper.getFieldName(InstanceInfo::getRuleType);
            Criteria ruleTypeCriteria = CriteriaWrapper.where(criteriaField).in(ruleTypeSet);
            criteriaList.add(ruleTypeCriteria);

            Criteria criteria = new Criteria().andOperator(criteriaList);
            List<InstanceInfo> instanceInfos = instanceService.selectListByQuery(new Query(criteria));
            if (null != instanceInfos) {
                for (InstanceInfo instanceInfo : instanceInfos) {
                    String key = instanceInfo.getRuleType() + instanceInfo.getInBandIp();
                    instanceInfoKeys.add(key);
                }
            }
        }


        // 查询模型组信息，获取模型组名
        CustomModelServiceImpl customModelService = (CustomModelServiceImpl) cmdbServiceManage.getModelService(CustomModelServiceImpl.ID);
        List<ModelInfo> modelInfos = customModelService.findByIds(new ArrayList<>(modelIds));
        Map<String, String> modelNameByIdMap = modelInfos.stream().collect(Collectors.toMap(ModelInfo::getId, ModelInfo::getModelName, (o1, o2) -> o1));

        ModelGroupServiceImpl groupService = (ModelGroupServiceImpl) cmdbServiceManage.getModelService(ModelGroupServiceImpl.ID);
        List<ModelGroupInfo> groupInfos = groupService.doSelectByIds(new ArrayList<>(groupIds));
        Map<String, String> groupNameByIdMap = groupInfos.stream().collect(Collectors.toMap(ModelGroupInfo::getId, ModelGroupInfo::getName, (o1, o2) -> o1));

        for (AssetsScanResultInfo resultInfo : assetsScanResultInfoList) {
            List<ScanResultSuccessInfo> successList = resultInfo.getSuccessList();
            if (CollectionUtil.isEmpty(successList)) {
                continue;
            }
            for (ScanResultSuccessInfo successInfo : successList) {
                String assetTypeName = "";
                String assetSubTypeName = "";

                if (StrUtil.isNotBlank(successInfo.getModelId())) {
                    // 匹配模型名
                    assetSubTypeName = modelNameByIdMap.get(successInfo.getModelId());
                }
                if (StrUtil.isBlank(assetTypeName) && StrUtil.isNotBlank(successInfo.getGroupId())) {
                    // 匹配组名
                    assetTypeName = groupNameByIdMap.get(successInfo.getGroupId());
                }
                successInfo.setAssetsTypeName(assetTypeName);
                successInfo.setAssetsSubTypeName(assetSubTypeName);

                String key = successInfo.getRuleType().name() + successInfo.getIpAddress();
                if (instanceInfoKeys.contains(key)) {
                    successInfo.setExist(true);
                }
            }
            if (CMDBTool.getFieldName(ScanResultSuccessInfo::getExist).equals(param.getSortName())) {
                boolean isAsc = ascSortModel.equals(param.getSortOrder());
                //列表字段排序
                sortListByField(successList, param.getSortName(), isAsc);
            }
        }

        return assetsScanResultInfoList;
    }

    private <T> void setIpLongIfNull(List<T> list, Function<T, Long> getIpLong, BiConsumer<T, Long> setIpLong, Function<T, String> getIpAddress) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        long count = list.stream().filter(item -> getIpLong.apply(item) != null).count();
        if (count != list.size()) {
            list.forEach(item -> {
                if (getIpLong.apply(item) == null) {
                    setIpLong.accept(item, IpUtil.ipToLong(getIpAddress.apply(item)));
                }
            });
        }
        list.sort(Comparator.comparing(getIpLong, Comparator.naturalOrder()));
    }

    public List<AssetsScanTotalResultInfo> getScanTotalResultInfos(String scanRuleId) throws Exception {
        Query query = new Query();
        if (!Strings.isNullOrEmpty(scanRuleId)) {
            query.addCriteria(CriteriaWrapper.where(AssetsScanTotalResultInfo::getId).is(scanRuleId));
        }
        List<AssetsScanTotalResultInfo> allList = totalResultMapper.find(query);
        return allList;
    }

    public void successDataExistCheck(List<ScanResultSuccessInfo> successList) throws Exception {
        if (CollectionUtil.isNotEmpty(successList)) {
            Set<String> ips = new HashSet<>();
            Set<String> groupIds = new HashSet<>();

            for (ScanResultSuccessInfo successInfo : successList) {
                String groupId = successInfo.getGroupId();
                String ip = successInfo.getIpAddress();
                ips.add(ip);
                groupIds.add(groupId);
            }
            InstanceServiceImpl instanceService = (InstanceServiceImpl) cmdbServiceManage.getModelService(InstanceServiceImpl.ID);
            InstanceSearchParam instanceSearchParam = new InstanceSearchParam();
            instanceSearchParam.setGroupIds(new ArrayList<>(groupIds));
            instanceSearchParam.setIps(new ArrayList<>(ips));
            List<InstanceInfo> instanceInfoList = instanceService.doSelectList(instanceSearchParam);

            Map<String, InstanceInfo> collect = instanceInfoList.stream().collect(Collectors.toMap(s -> s.getGroupId() + "_" + s.getInBandIp(), s -> s, (
                    value1, value2) -> {
                return value2;
            }));

            for (ScanResultSuccessInfo successInfo : successList) {
                String groupId = successInfo.getGroupId();
                String ip = successInfo.getIpAddress();
                String key = groupId + "_" + ip;
                if (collect != null && collect.containsKey(key)) {
                    InstanceInfo instanceInfo = collect.get(key);
                    Integer serverId = instanceInfo.getMonitorServerId();
                    Date createTime = instanceInfo.getCreateTime();
                    String hostId = instanceInfo.getMonitorTypeId();
                    if (intValueConvert(serverId) != 0 && !Strings.isNullOrEmpty(hostId)) {
                        successInfo.setInsertType(insertTypeManage);
                    } else {
                        successInfo.setInsertType(insertTypeAdd);
                    }
                    successInfo.setAddTime(createTime);
                    successInfo.setExist(true);
                }
            }
        }
    }


    public void doBatchUpdateByRuleIdAndIp(String id, List<String> ips) throws Exception {
        String name = PropertyType.formatSearchKey(CMDBTool.getFieldName(AssetsScanResultInfo::getSuccessList), CMDBTool.getFieldName(ScanResultSuccessInfo::getIpAddress));
        Criteria criteria = CriteriaWrapper.where(AssetsScanResultInfo::getScanRuleId).is(id).and(name).in(ips);
        Query query = new Query(criteria);

        Update update = new Update();
        //successList.$ipAddress
        String key = CMDBTool.getFieldName(AssetsScanResultInfo::getSuccessList) +
                MongoConstant.FieldSep + MongoConstant.indexKey + MongoConstant.FieldSep +
                CMDBTool.getFieldName(ScanResultSuccessInfo::getExist);

        update.set(key, true);

        assetsScanResultMapper.updateMulti(query, update);
    }

    @Data
    static class AssetsScanTimeProgress {
        public Long startTimestamp;
        public Long endTimestamp;
    }

    @Data
    static class AssetsBatchManageDTO {
        private String id;
        private AssetsDiscoverInfo info;
        private List<String> ips;
    }

    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Set<Object> seen = new HashSet<>();
        return t -> seen.add(keyExtractor.apply(t));
    }
}
